# Promise

# Promise A+

实现一个符合 Promise A+规范的自定义 Promise,该实现可通过promises-aplus-tests (opens new window)全部测试

class MyPromise {
  status = 'pending'
  value = undefined
  _fulfilled = []
  _rejected = []

  constructor(fn) {
    const resolve = val => {
      setTimeout(() => {
        if (this.status !== 'pending') {
          return
        }
        if (val instanceof MyPromise) {
          // 如果 value 是个 Promise,递归执行
          return val.then(resolve, reject)
        }
        this.value = val
        this.status = 'fulfilled'
        while (this._fulfilled.length) {
          this._fulfilled.shift()()
        }
      })
    }
    const reject = err => {
      setTimeout(() => {
        if (this.status !== 'pending') {
          return
        }
        this.value = err
        this.status = 'rejected'
        while (this._rejected.length) {
          this._rejected.shift()()
        }
      })
    }
    try {
      fn(resolve, reject)
    } catch (e) {
      reject(e)
    }
  }

  then(onFulfilled, onRejected) {
    let self = this
    let p = new MyPromise((resolve, reject) => {
      if (typeof onFulfilled !== 'function') {
        onFulfilled = val => val
      }
      if (typeof onRejected !== 'function') {
        onRejected = err => {
          throw err
        }
      }

      const fulfilledFn = function () {
        try {
          let r = onFulfilled(self.value)
          resolveValue(r, p, resolve, reject)
        } catch (err) {
          reject(err)
        }
      }
      const rejectedFn = function () {
        try {
          let r = onRejected(self.value)
          resolveValue(r, p, resolve, reject)
        } catch (err) {
          reject(err)
        }
      }
      if (this.status === 'pending') {
        this._fulfilled.push(fulfilledFn)
        this._rejected.push(rejectedFn)
      } else if (this.status === 'fulfilled') {
        setTimeout(() => {
          fulfilledFn()
        })
      } else {
        setTimeout(() => {
          rejectedFn()
        })
      }
    })
    return p
  }
  catch(fn) {
    return this.then(null, fn)
  }
}

function resolveValue(result, currentPromise, resolve, reject) {
  if (result === currentPromise) {
    return reject(new TypeError('Error'))
  }

  if (result instanceof MyPromise) {
    result.then(value => {
      resolveValue(value, currentPromise, resolve, reject)
    }, reject)
    return
  }

  let called = false
  if (result !== null && (typeof result === 'object' || typeof result === 'function')) {
    try {
      let then = result.then
      if (typeof then === 'function') {
        then.call(
          result,
          y => {
            if (called) return
            called = true
            resolveValue(y, currentPromise, resolve, reject)
          },
          e => {
            if (called) return
            called = true
            reject(e)
          }
        )
      } else {
        resolve(result)
      }
    } catch (e) {
      if (called) return
      called = true
      reject(e)
    }
  } else {
    resolve(result)
  }
}

// 用于跑Promise的测试(promises-aplus-tests)用例
MyPromise.deferred = function () {
  const deferred = {}
  deferred.promise = new MyPromise((resolve, reject) => {
    deferred.resolve = resolve
    deferred.reject = reject
  })
  return deferred
}

module.exports = MyPromise

# 实现 Promise 的静态方法

# Promise.all

/**
 * 实现Promise.all
 * 参数未验证,主要实现原理
 */
Promise.all = function(promiseArray) {
  return new Promise((resolve, reject) => {
    let i = 0
    const result = []
    promiseArray.forEach((p, index) => {
      p.then(
        val => {
          i++
          result[index] = val
          if (i === promiseArray.length) {
            resolve(result)
          }
        },
        err => reject(err)
      )
    })
  })
}

# Promise.race

/**
 * 实现Promise.race
 * 参数未验证,主要实现原理
 */
Promise.race = function(promiseArray) {
  return new Promise((resolve, reject) => {
    let i = 0
    promiseArray.forEach((p, index) => {
      p.then(
        val => resolve(val),
        err => reject(err)
      )
    })
  })
}

# Promise.allSettled

/**
 * 实现Promise.allSettled
 * 参数未验证,主要实现原理
 */
Promise.allSettled = function(promiseArray) {
  return new Promise((resolve, reject) => {
    let i = 0
    const result = []
    promiseArray.forEach((p, index) => {
      p.then(
        val => {
          i++
          result[index] = {
            status: 'fulfilled',
            value: val
          }
          if (i === promiseArray.length) {
            resolve(result)
          }
        },
        err => {
          i++
          result[index] = {
            status: 'rejected',
            value: err
          }
          if (i === promiseArray.length) {
            resolve(result)
          }
        }
      )
    })
  })
}

# 并行 Promise 与串行 Promise

一批函数执行后,如果有一个函数返回成功,则整个任务成功。分为串行和并行两种方式。使用场景举例:扫描服务器端口 0-65535。每次并发扫描10个端口,直到找到一个可用端口。

/**
 * 给Promise添加parallelOnce和serialOnce两个静态方法
 */
export default class P extends Promise {
  /**
   * 并行执行多个Promise任务
   * 当有一个fulfilled就让整个队列成功
   * 如果没有一个任务fulfilled就rejected
   * @param {Function[]} items
   */
  static parallelOnce(items) {
    return new Promise((resolve, reject) => {
      let i = 0
      items.forEach(item => {
        item()
          .then(val => {
            resolve(val)
          })
          .catch(err => {
            i++
            if (i === items.length) {
              reject(new Error('ALL_REJECTED'))
            }
          })
      })
    })
  }

  /**
   * 串行执行多个Promise任务
   * 当有一个fulfilled就让整个队列成功
   * 如果没有一个任务fulfilled就rejected
   * @param {Function[]} items
   */
  static async serialOnce(items) {
    for (let item of items) {
      try {
        return await item()
      } catch (e) {}
    }

    throw new Error('ALL_REJECTED')
  }
}